﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using EntModApp.EntServices.Validation;
using EntModApp.EntServices.Presentation;

namespace EntModApp.EntServices.PresentationLogic
{
  public abstract class ViewModelBase<TModel> : SyncNotifyObject
  {
    public ViewModelBase() : this(false) 
    { 
      Initialize(); 
    }

    public ViewModelBase(bool autoLoadModel)
    {
      if (autoLoadModel)
        Model = LoadModel();
      Initialize();
    }

    public ViewModelBase(TModel _Model_)
    {
      Model = _Model_;
      Initialize();
    }

    private void Initialize()
    {
      RegisterQuickFiring(() => IsValid);
      RegisterQuickFiring(() => ValidationResults);
    }

    protected abstract TModel LoadModel();

    private TModel _Model;

    public TModel Model
    {
      get { return _Model; }
      set
      {
        if (Equals(_Model, value)) return;
        UnwireModel();
        _Model = value;
        WireModel();
        OnPropertyChanged(() => Model);
      }
    }

    public abstract IEnumerable<ILogicalCommand> AllCommands { get; }

    protected virtual void WireModel()
    {
    }

    protected virtual void UnwireModel()
    {
    }

    private static HashSet<string> SpecialProperties;
    static ViewModelBase()
    {
      SpecialProperties = new HashSet<string> 
      {
        "IsValid",
        "ValidationResults",
      };
    }

    public bool IsValid
    {
      get
      {
        SetValidationResults(ValidatorHelper.GetValidionResults(this, GetValidationContext(), true));
        return ValidatorHelper.IsValid(_ROValidationResults);
      }
    }

    public void Validate()
    {
      SetValidationResults(ValidatorHelper.GetValidionResults(this, GetValidationContext(), true));
      ValidatorHelper.Validate(_ROValidationResults);
    }

    private ObservableCollection<ValidationResult> _ValidationResults;
    private ReadOnlyObservableCollection<ValidationResult> _ROValidationResults;
    public ReadOnlyObservableCollection<ValidationResult> ValidationResults
    {
      get
      {
        if (_ROValidationResults == null)
        {
          if (_ValidationResults == null)
            _ValidationResults = new ObservableCollection<ValidationResult>();
          _ROValidationResults = new ReadOnlyObservableCollection<ValidationResult>(_ValidationResults);
        }
        return _ROValidationResults;
      }
    }

    protected void AddError(ValidationResult error)
    {
      if (error == null) throw new ArgumentNullException("error");
      if (_ValidationResults == null)
      {
        _ValidationResults = new ObservableCollection<ValidationResult> { error };
        OnPropertyChanged(() => ValidationResults);
      }
      else
      {
        _ValidationResults.Add(error);
      }
    }
    protected void AddError(string error)
    {
      AddError(new ValidationResult(error));
    }

    private void SetValidationResults(IEnumerable<ValidationResult> results)
    {
      if (_ValidationResults == null)
      {
        _ValidationResults = new ObservableCollection<ValidationResult>(results);
        if (_ValidationResults.Count > 0)
        {
          OnPropertyChanged(() => IsValid);
          OnPropertyChanged(() => ValidationResults);
        }
      }
      else
      {
        var wasEmpty = _ValidationResults.Count == 0;
        _ValidationResults.Clear();
        foreach (var item in results)
          _ValidationResults.Add(item);
        if (wasEmpty != (_ValidationResults.Count == 0))
          OnPropertyChanged(() => IsValid);
      }
    }

    protected override void TriggerPropertyChanged(PropertyChangedEventArgs args)
    {
      base.TriggerPropertyChanged(args);
      foreach (var command in AllCommands.OfType<ITriggerLogicalCommand>().Where(c => c.IsDependentProperty(args.PropertyName)))
        command.TriggerCanExecuteChanged();
    }

    protected virtual ValidationContext GetValidationContext()
    {
      return null;
    }
  }

  public abstract class ViewModelBase : SyncNotifyObject
  {
    private static HashSet<string> SpecialProperties;
    static ViewModelBase()
    {
      SpecialProperties = new HashSet<string> 
      {
        "IsValid",
        "ValidationResults",
      };
    }

    public ViewModelBase()
    {
      Initialize();
    }

    private void Initialize()
    {
      RegisterQuickFiring(() => IsValid);
      RegisterQuickFiring(() => ValidationResults);
    }

    public abstract IEnumerable<ILogicalCommand> AllCommands { get; }

    public bool IsValid
    {
      get
      {
        SetValidationResults(ValidatorHelper.GetValidionResults(this, GetValidationContext(), true));
        return ValidatorHelper.IsValid(_ValidationResults);
      }
    }

    public void Validate()
    {
      SetValidationResults(ValidatorHelper.GetValidionResults(this, GetValidationContext(), true));
      ValidatorHelper.Validate(_ValidationResults);
    }

    private ObservableCollection<ValidationResult> _ValidationResults;
    private ReadOnlyObservableCollection<ValidationResult> _ROValidationResults;
    public ReadOnlyObservableCollection<ValidationResult> ValidationResults
    {
      get
      {
        GlobalSynchronizationContext.Current.Send(o =>
          {
            if (_ROValidationResults == null)
            {
              if (_ValidationResults == null)
                _ValidationResults = new ObservableCollection<ValidationResult>();
              _ROValidationResults = new ReadOnlyObservableCollection<ValidationResult>(_ValidationResults);
            }
          }, null);
        return _ROValidationResults;
      }
    }

    protected void AddError(ValidationResult error)
    {
      if (error == null) throw new ArgumentNullException("error");
      GlobalSynchronizationContext.Current.Send(o =>
        {
          if (_ValidationResults == null)
          {
            _ValidationResults = new ObservableCollection<ValidationResult> { error };
            OnPropertyChanged(() => ValidationResults);
          }
          else
          {
            _ValidationResults.Add(error);
          }
        }, null);
    }
    protected void AddError(string error)
    {
      AddError(new ValidationResult(error));
    }

    private void SetValidationResults(IEnumerable<ValidationResult> results)
    {
      GlobalSynchronizationContext.Current.Send(o =>
        {
          if (_ValidationResults == null)
          {
            _ValidationResults = new ObservableCollection<ValidationResult>(results);
            OnPropertyChanged(() => ValidationResults);
          }
          else
          {
            _ValidationResults.Clear();
            foreach (var item in results)
              _ValidationResults.Add(item);
          }
        }, null);
    }

    protected override void TriggerPropertyChanged(PropertyChangedEventArgs args)
    {
      base.TriggerPropertyChanged(args);
      foreach (var command in AllCommands.OfType<ITriggerLogicalCommand>().Where(c => c.IsDependentProperty(args.PropertyName)))
        command.TriggerCanExecuteChanged();
    }

    protected virtual ValidationContext GetValidationContext()
    {
      return null;
    }

  }
}
